package order

import (
	"context"
	"strconv"
	"strings"

	"mall/common/globalkey"
	"mall/common/modelx"
	"mall/common/moneyx"
	"mall/common/resultx"
	"mall/common/utils/pointy"
	"mall/service/order/model"
	"mall/service/order/rpc/internal/svc"
	"mall/service/order/rpc/types/order"

	// "github.com/sirupsen/logrus"
	"github.com/zeromicro/go-zero/core/logx"
)

type GetOrderListLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewGetOrderListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetOrderListLogic {
	return &GetOrderListLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func (l *GetOrderListLogic) GetOrderList(in *order.BaseListReq) (*order.OrderListResp, error) {
	if in.Filter == nil || len(*in.Filter) == 0 {
		return nil, resultx.StatusError(resultx.REUQEST_PARAM_ERROR, "")
	}

	preloadMap, eagerLoadMap := modelx.ParsePreloadAndEagerLoad(in.Preload, in.EagerLoad)

	// logrus.Info(fmt.Sprintf("GetOrderList preloadMap: %+v", preloadMap))
	// map[order:[Id Sn TradeState Quantity RetailAmount Freight DiscountAmount AdjustAmount ActualAmount Amount RefundAmount Remark CreatedAt UpdatedAt PaidAt ShippedAt DeliveredAt ConfirmedAt ReviewedAt ClosedAt Growth Integrals] orderItem:[Id SkuId Sku ImageId ImageUrl ProductId EntryId Title Slug ProductType BrandId BrandTitle BrandSlug RetailPrice Quantity TradeState Status RetailAmount Freight DiscountAmount AdjustAmount ActualAmount RefundAmount RefundQuantity]]
	// logrus.Info(fmt.Sprintf("GetOrderList eagerLoadMap: %+v", eagerLoadMap))
	// map[order:[orderItem]]

	orderEntityName := model.RpcEntityNames.Order
	_, hasSettingMap := model.RpcEntityPreloadMap[orderEntityName]
	if !hasSettingMap {
		return &order.OrderListResp{}, nil
	}

	orderRepositoryName := model.RepositoryNames.Order
	columnMap, hasColumnMap := model.RepositoryPreloadMap[orderRepositoryName]
	if !hasColumnMap {
		return &order.OrderListResp{}, nil
	}

	var selectCols []string
	if preloadFields, hasPreloadFields := preloadMap[orderEntityName]; hasPreloadFields {
		selectCols = modelx.ParseSelectColumns(preloadFields, columnMap)
	}

	filterMap := modelx.ParseFilter(in.Filter, columnMap)
	// logrus.Info(fmt.Sprintf("GetOrderList filterMap: %+v", filterMap))
	// map[trade_state:map[moreThan:[4]] user_id:map[equalTo:[10021]]]
	if filterMap == nil {
		return nil, resultx.StatusError(resultx.REUQEST_PARAM_ERROR, "")
	}

	orderBy := modelx.ParseSorter(in.Sorter, columnMap)
	// logrus.Info(fmt.Sprintf("GetOrderList orderBy: %s", orderBy))
	if len(orderBy) == 0 {
		orderBy = model.OrderColumns.ID + " DESC"
	}

	if in.Search != nil && len(*in.Search) > 12 {
		filterMap[model.OrderColumns.OrderSN] = map[string][]any{
			"startWith": []any{*in.Search},
		}
	}

	res, count, err := l.svcCtx.Order.GetList(l.ctx, model.M{
		"offset":  in.Offset,
		"limit":   in.Limit,
		"orderBy": orderBy,
		"select":  strings.Join(selectCols, ","),
	}, 2, filterMap)
	if err != nil {
		return nil, err
	}

	hasNextPage, hasPreviousPage := modelx.ParseCount(count)
	resp := &order.OrderListResp{
		HasNextPage:     &hasNextPage,
		HasPreviousPage: &hasPreviousPage,
	}

	var total int64
	if len(res) > 0 {
		var ids []any
		for _, v := range res {
			ids = append(ids, v.ID)
		}

		subEntitySettingMap := make(map[string]map[string]*model.ColumnSetting)
		if eagerLoadEntities, hasEagerLoadEntities := eagerLoadMap[orderEntityName]; hasEagerLoadEntities {
			for _, entity := range eagerLoadEntities {
				if entity == "total" {
					_, total, err = l.svcCtx.Order.GetList(l.ctx, model.M{}, 0, filterMap)
					if err != nil {
						total = 0
					}
					continue
				}

				if entitySettingMap, hasEntitySettingMap := model.RpcEntityPreloadMap[entity]; hasEntitySettingMap {
					subEntitySettingMap[entity] = entitySettingMap
				}
			}
		}

		orderSkuEntityName := model.RpcEntityNames.OrderItem
		orderSkuRepositoryName := model.RepositoryNames.OrderItem
		idSkuMap := make(map[uint64][]*order.OrderSkuInfo)
		if _, hasEntitySettingMap := subEntitySettingMap[orderSkuEntityName]; hasEntitySettingMap {
			subPreloadFields, hasSubPreloadFields := preloadMap[orderSkuEntityName]
			subColumnMap, hasSubColumnMap := model.RepositoryPreloadMap[orderSkuRepositoryName]
			if hasSubPreloadFields && hasSubColumnMap {
				var subSelectCols []string = modelx.ParseSelectColumns(subPreloadFields, subColumnMap)
				subSelectCols = append(subSelectCols, model.OrderItemColumns.OrderID)

				subFilterMap := make(map[string]map[string][]any)
				subFilterMap[model.OrderItemColumns.OrderID] = map[string][]any{
					"in": ids,
				}
				list, _, err := l.svcCtx.Order.GetItemList(l.ctx, model.M{
					"select": strings.Join(subSelectCols, ","),
				}, -1, subFilterMap)
				if err != nil {
					return nil, err
				}

				if len(list) > 0 {
					for _, v := range list {
						idSkuMap[v.OrderID] = append(idSkuMap[v.OrderID], &order.OrderSkuInfo{
							Id:             pointy.GetPointer(v.OrderItemID),
							OrderId:        pointy.GetPointer(v.OrderID),
							ProductCode:    pointy.GetPointer(v.ProductCode),
							SkuId:          pointy.GetPointer(v.SkuID),
							SkuCode:        pointy.GetPointer(v.SkuCode),
							Sku:            pointy.GetPointer(v.Sku),
							ImageId:        pointy.GetPointer(strconv.FormatInt(int64(v.SkuImageID), 10)),
							ImageUrl:       pointy.GetPointer(v.SkuImageURL),
							ProductId:      pointy.GetPointer(v.ProductID),
							EntryId:        pointy.GetPointer(v.EntryID),
							Title:          pointy.GetPointer(v.Title),
							Slug:           pointy.GetPointer(v.Slug),
							ProductType:    pointy.GetPointer(uint32(v.ProductType)),
							BrandId:        pointy.GetPointer(v.BrandID),
							BrandTitle:     pointy.GetPointer(v.BrandTitle),
							BrandSlug:      pointy.GetPointer(v.BrandSlug),
							GemPrice:       pointy.GetPointer(moneyx.FormatGem(int64(v.GemPrice))),
							RetailPrice:    pointy.GetPointer(moneyx.FormatAmount(int64(v.RetailPrice))),
							Quantity:       pointy.GetPointer(uint32(v.Quantity)),
							Weight:         pointy.GetPointer(moneyx.FormatWeight(int64(v.Weight))),
							TradeState:     pointy.GetPointer(uint32(v.TradeState)),
							Status:         pointy.GetPointer(uint32(v.Status)),
							GemAmount:      pointy.GetPointer(moneyx.FormatGem(int64(v.GemAmount))),
							RetailAmount:   pointy.GetPointer(moneyx.FormatAmount(int64(v.RetailAmount))),
							DiscountAmount: pointy.GetPointer(moneyx.FormatAmount(int64(v.DiscountAmount))),
							AdjustAmount:   pointy.GetPointer(moneyx.FormatAmount(int64(v.AdjustAmount))),
							ActualAmount:   pointy.GetPointer(moneyx.FormatAmount(int64(v.ActualAmount))),
							RefundAmount:   pointy.GetPointer(moneyx.FormatAmount(int64(v.RefundAmount))),
							RefundQuantity: pointy.GetPointer(uint32(v.RefundQuantity)),
						})
					}
				}
			}
		}

		orderAddressEntityName := model.RpcEntityNames.OrderAddress
		orderAddressRepositoryName := model.RepositoryNames.OrderAddress
		idAddressMap := make(map[uint64][]*order.OrderAddressInfo)
		if _, hasEntitySettingMap := subEntitySettingMap[orderAddressEntityName]; hasEntitySettingMap {
			subPreloadFields, hasSubPreloadFields := preloadMap[orderAddressEntityName]
			subColumnMap, hasSubColumnMap := model.RepositoryPreloadMap[orderAddressRepositoryName]
			if hasSubPreloadFields && hasSubColumnMap {
				var subSelectCols []string = modelx.ParseSelectColumns(subPreloadFields, subColumnMap)
				subSelectCols = append(subSelectCols, model.OrderAddressColumns.OrderID)

				subFilterMap := make(map[string]map[string][]any)
				subFilterMap[model.OrderAddressColumns.OrderID] = map[string][]any{
					"in": ids,
				}
				subFilterMap[model.OrderAddressColumns.Status] = map[string][]any{
					"equalTo": []any{globalkey.StatusEnabled},
				}
				list, _, err := l.svcCtx.Order.GetAddressList(l.ctx, model.M{
					"select": strings.Join(subSelectCols, ","),
				}, -1, subFilterMap)
				if err != nil {
					return nil, err
				}

				if len(list) > 0 {
					for _, v := range list {
						idAddressMap[v.OrderID] = append(idAddressMap[v.OrderID], &order.OrderAddressInfo{
							Id:         &v.OrderAddressID,
							OrderId:    &v.OrderID,
							UserId:     &v.UserID,
							Consignee:  &v.Consignee,
							Mobile:     &v.Mobile,
							Province:   &v.Province,
							City:       &v.City,
							District:   &v.District,
							Street:     &v.Street,
							Address:    &v.DetailedAddress,
							PostalCode: &v.PostalCode,
							RegionCode: &v.RegionCode,
							Label:      &v.Label,
							Status:     pointy.GetPointer(uint32(v.Status)),
						})
					}
				}
			}
		}

		orderAdjustmentEntityName := model.RpcEntityNames.OrderAdjustment
		orderAdjustmentRepositoryName := model.RepositoryNames.OrderAdjustment
		idAdjustmentMap := make(map[uint64][]*order.OrderAdjustmentInfo)
		if _, hasEntitySettingMap := subEntitySettingMap[orderAdjustmentEntityName]; hasEntitySettingMap {
			subPreloadFields, hasSubPreloadFields := preloadMap[orderAdjustmentEntityName]
			subColumnMap, hasSubColumnMap := model.RepositoryPreloadMap[orderAdjustmentRepositoryName]
			if hasSubPreloadFields && hasSubColumnMap {
				var subSelectCols []string = modelx.ParseSelectColumns(subPreloadFields, subColumnMap)
				subSelectCols = append(subSelectCols, model.OrderAdjustmentColumns.OrderID)

				subFilterMap := make(map[string]map[string][]any)
				subFilterMap[model.OrderAdjustmentColumns.OrderID] = map[string][]any{
					"in": ids,
				}
				subFilterMap[model.OrderAdjustmentColumns.Status] = map[string][]any{
					"equalTo": []any{globalkey.StatusEnabled},
				}
				list, _, err := l.svcCtx.Order.GetAdjustmentList(l.ctx, model.M{
					"select": strings.Join(subSelectCols, ","),
				}, -1, subFilterMap)
				if err != nil {
					return nil, err
				}

				if len(list) > 0 {
					for _, v := range list {
						idAdjustmentMap[v.OrderID] = append(idAdjustmentMap[v.OrderID], &order.OrderAdjustmentInfo{
							Id:         &v.OrderAdjustmentID,
							OrderId:    &v.OrderID,
							Amount:     pointy.GetPointer(moneyx.FormatAmount(int64(v.Amount))),
							Memo:       &v.Memo,
							Status:     pointy.GetPointer(uint32(v.Status)),
							OperatedBy: &v.OperatedBy,
							OperatedAt: pointy.GetPointer(modelx.FormatNullDotTime(v.OperatedAt, "")),
						})
					}
				}
			}
		}

		orderDiscountEntityName := model.RpcEntityNames.OrderDiscount
		orderDiscountRepositoryName := model.RepositoryNames.OrderDiscount
		idDiscountMap := make(map[uint64][]*order.OrderDiscountInfo)
		if _, hasEntitySettingMap := subEntitySettingMap[orderDiscountEntityName]; hasEntitySettingMap {
			subPreloadFields, hasSubPreloadFields := preloadMap[orderDiscountEntityName]
			subColumnMap, hasSubColumnMap := model.RepositoryPreloadMap[orderDiscountRepositoryName]
			if hasSubPreloadFields && hasSubColumnMap {
				var subSelectCols []string = modelx.ParseSelectColumns(subPreloadFields, subColumnMap)
				subSelectCols = append(subSelectCols, model.OrderDiscountColumns.OrderID)

				subFilterMap := make(map[string]map[string][]any)
				subFilterMap[model.OrderDiscountColumns.OrderID] = map[string][]any{
					"in": ids,
				}
				list, _, err := l.svcCtx.Order.GetDiscountList(l.ctx, model.M{
					"select": strings.Join(subSelectCols, ","),
				}, -1, subFilterMap)
				if err != nil {
					return nil, err
				}

				if len(list) > 0 {
					for _, v := range list {
						idDiscountMap[v.OrderID] = append(idDiscountMap[v.OrderID], &order.OrderDiscountInfo{
							Id:               &v.OrderDiscountID,
							OrderId:          &v.OrderID,
							SkuId:            &v.SkuID,
							ProductId:        &v.ProductID,
							Title:            &v.Title,
							Slug:             &v.Slug,
							ActivityId:       &v.ActivityID,
							ActivityRuleId:   &v.ActivityRuleID,
							CouponTemplateId: &v.CouponTemplateID,
							CouponId:         &v.CouponID,
							GrouponAmount:    pointy.GetPointer(moneyx.FormatAmount(int64(v.GrouponAmount))),
							WholesaleAmount:  pointy.GetPointer(moneyx.FormatAmount(int64(v.WholesaleAmount))),
							CouponAmount:     pointy.GetPointer(moneyx.FormatAmount(int64(v.CouponAmount))),
							ActivityAmount:   pointy.GetPointer(moneyx.FormatAmount(int64(v.ActivityAmount))),
							IntegralAmount:   pointy.GetPointer(moneyx.FormatAmount(int64(v.IntegralAmount))),
							DiscountAmount:   pointy.GetPointer(moneyx.FormatAmount(int64(v.DiscountAmount))),
							Integrals:        &v.Integrals,
						})
					}
				}
			}
		}

		orderPaymentEntityName := model.RpcEntityNames.OrderPayment
		orderPaymentRepositoryName := model.RepositoryNames.OrderPayment
		idPaymentMap := make(map[uint64][]*order.OrderPaymentInfo)
		if _, hasEntitySettingMap := subEntitySettingMap[orderPaymentEntityName]; hasEntitySettingMap {
			subPreloadFields, hasSubPreloadFields := preloadMap[orderPaymentEntityName]
			subColumnMap, hasSubColumnMap := model.RepositoryPreloadMap[orderPaymentRepositoryName]
			if hasSubPreloadFields && hasSubColumnMap {
				var subSelectCols []string = modelx.ParseSelectColumns(subPreloadFields, subColumnMap)
				subSelectCols = append(subSelectCols, model.OrderPaymentColumns.OrderID)

				subFilterMap := make(map[string]map[string][]any)
				subFilterMap[model.OrderPaymentColumns.OrderID] = map[string][]any{
					"in": ids,
				}
				subFilterMap[model.OrderPaymentColumns.Status] = map[string][]any{
					"equalTo": []any{globalkey.PaymentStatusPaid},
				}
				list, _, err := l.svcCtx.Order.GetPaymentList(l.ctx, model.M{
					"select":  strings.Join(subSelectCols, ","),
					"orderBy": model.OrderPaymentColumns.OrderPaymentID + " DESC",
				}, -1, subFilterMap)
				if err != nil {
					return nil, err
				}

				if len(list) > 0 {
					for _, v := range list {
						idPaymentMap[v.OrderID] = append(idPaymentMap[v.OrderID], &order.OrderPaymentInfo{
							Id:            pointy.GetPointer(v.OrderPaymentID),
							OrderId:       pointy.GetPointer(v.OrderID),
							No:            pointy.GetPointer(v.No),
							TransactionID: pointy.GetPointer(v.TransactionID),
							Amount:        pointy.GetPointer(moneyx.FormatAmount(int64(v.Amount))),
							Type:          pointy.GetPointer(uint32(v.Type)),
							Name:          pointy.GetPointer(v.Name),
							Code:          pointy.GetPointer(v.Code),
							Status:        pointy.GetPointer(uint32(v.Status)),
							PaidAt:        pointy.GetPointer(modelx.FormatNullDotTime(v.PaidAt, "")),
						})
					}
				}
			}
		}

		orderShipmentEntityName := model.RpcEntityNames.OrderShipment
		orderShipmentRepositoryName := model.RepositoryNames.OrderShipment
		idShipmentMap := make(map[uint64][]*order.OrderShipmentInfo)
		if _, hasEntitySettingMap := subEntitySettingMap[orderShipmentEntityName]; hasEntitySettingMap {
			subPreloadFields, hasSubPreloadFields := preloadMap[orderShipmentEntityName]
			subColumnMap, hasSubColumnMap := model.RepositoryPreloadMap[orderShipmentRepositoryName]
			if hasSubPreloadFields && hasSubColumnMap {
				var subSelectCols []string = modelx.ParseSelectColumns(subPreloadFields, subColumnMap)
				subSelectCols = append(subSelectCols, model.OrderShipmentColumns.OrderID)

				subFilterMap := make(map[string]map[string][]any)
				subFilterMap[model.OrderShipmentColumns.OrderID] = map[string][]any{
					"in": ids,
				}
				subFilterMap[model.OrderShipmentColumns.Status] = map[string][]any{
					"equalTo": []any{globalkey.StatusEnabled},
				}
				list, _, err := l.svcCtx.Order.GetShipmentList(l.ctx, model.M{
					"select": strings.Join(subSelectCols, ","),
				}, -1, subFilterMap)
				if err != nil {
					return nil, err
				}

				if len(list) > 0 {
					for _, v := range list {
						idShipmentMap[v.OrderID] = append(idShipmentMap[v.OrderID], &order.OrderShipmentInfo{
							Id:        &v.OrderShipmentID,
							OrderId:   &v.OrderID,
							No:        &v.No,
							Type:      pointy.GetPointer(uint32(v.Type)),
							Name:      &v.Name,
							Code:      &v.Code,
							Status:    pointy.GetPointer(uint32(v.Status)),
							UpdatedAt: pointy.GetPointer(modelx.FormatTime(v.UpdatedAt, "")),
						})
					}
				}
			}
		}

		for _, v := range res {
			var skus []*order.OrderSkuInfo
			if ss, ok := idSkuMap[v.ID]; ok {
				skus = ss[:]
			}
			var addresses []*order.OrderAddressInfo
			if as, ok := idAddressMap[v.ID]; ok {
				addresses = as[:]
			}
			var adjustments []*order.OrderAdjustmentInfo
			if as, ok := idAdjustmentMap[v.ID]; ok {
				adjustments = as[:]
			}
			var discounts []*order.OrderDiscountInfo
			if ps, ok := idDiscountMap[v.ID]; ok {
				discounts = ps[:]
			}
			var payments []*order.OrderPaymentInfo
			if ps, ok := idPaymentMap[v.ID]; ok {
				payments = ps[:]
			}
			var shipments []*order.OrderShipmentInfo
			if ss, ok := idShipmentMap[v.ID]; ok {
				shipments = ss[:]
			}
			resp.List = append(resp.List, &order.OrderInfo{
				Id:              &v.ID,
				ParentId:        &v.ParentID,
				UserId:          &v.UserID,
				UserRank:        &v.UserRank,
				Buyer:           &v.Buyer,
				Sn:              &v.OrderSN,
				OrderType:       pointy.GetPointer(uint32(v.OrderType)),
				SourceType:      pointy.GetPointer(uint32(v.SourceType)),
				Number:          pointy.GetPointer(uint32(v.Quantity)),
				GemAmount:       pointy.GetPointer(moneyx.FormatGem(int64(v.GemAmount))),
				RetailAmount:    pointy.GetPointer(moneyx.FormatAmount(int64(v.RetailAmount))),
				Freight:         pointy.GetPointer(moneyx.FormatAmount(int64(v.Freight))),
				DiscountAmount:  pointy.GetPointer(moneyx.FormatAmount(int64(v.DiscountAmount))),
				AdjustAmount:    pointy.GetPointer(moneyx.FormatAmount(int64(v.AdjustAmount))),
				ActualAmount:    pointy.GetPointer(moneyx.FormatAmount(int64(v.ActualAmount))),
				DepositAmount:   pointy.GetPointer(moneyx.FormatAmount(int64(v.DepositAmount))),
				BalanceAmount:   pointy.GetPointer(moneyx.FormatAmount(int64(v.BalanceAmount))),
				RemainingAmount: pointy.GetPointer(moneyx.FormatAmount(int64(v.RemainingAmount))),
				RefundAmount:    pointy.GetPointer(moneyx.FormatAmount(int64(v.RefundAmount))),
				Gems:            &v.Gems,
				TradeState:      pointy.GetPointer(uint32(v.TradeState)),
				Remark:          &v.Remark,
				SellerMemo:      &v.SellerMemo,
				CreatedAt:       pointy.GetPointer(modelx.FormatTime(v.CreatedAt, "")),
				UpdatedAt:       pointy.GetPointer(modelx.FormatTime(v.UpdatedAt, "")),
				DeletedAt:       pointy.GetPointer(modelx.FormatNullDotTime(v.DeletedAt, "")),
				PaidAt:          pointy.GetPointer(modelx.FormatNullDotTime(v.PaidAt, "")),
				ShippedAt:       pointy.GetPointer(modelx.FormatNullDotTime(v.ShippedAt, "")),
				DeliveredAt:     pointy.GetPointer(modelx.FormatNullDotTime(v.DeliveredAt, "")),
				ConfirmedAt:     pointy.GetPointer(modelx.FormatNullDotTime(v.ConfirmedAt, "")),
				ReviewedAt:      pointy.GetPointer(modelx.FormatNullDotTime(v.ReviewedAt, "")),
				FulfilledAt:     pointy.GetPointer(modelx.FormatNullDotTime(v.FulfilledAt, "")),
				ClosedAt:        pointy.GetPointer(modelx.FormatNullDotTime(v.ClosedAt, "")),
				AutoComfirmDays: pointy.GetPointer(uint32(v.AutoComfirmDays)),
				Growth:          pointy.GetPointer(uint32(v.Growth)),
				Integrals:       pointy.GetPointer(uint32(v.Integrals)),
				Skus:            skus,
				Addresses:       addresses,
				Adjustments:     adjustments,
				Discounts:       discounts,
				Payments:        payments,
				Shipments:       shipments,
			})
		}
	}

	resp.Total = uint64(total)

	return resp, nil
}
